package junior.util.date;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import junior.util.string.Strings;

/**
 * 时间间隔<br/>
 * <br/>
 * <b>用例</b>，获取当前时间往后1天8小时后的时间：<br/>
 * <code>new TimeInterval().addDays(1).addHours(8).{@link #getTo()}</code><br/>
 * <b>用例</b>，获取两个时间的间隔：<br/>
 * <code>new TimeInterval(fromDate,toDate).{@link #getAbsInterval()}</code><br/>
 * <b>用例</b>，离本月结束还有多少天：<br/>
 * <code>TimeInterval.newWeekInterval(new Date(), 0).setFrom(new Date()).{@link #getDays()}</code><br/>
 * <b>用例</b>，上个月所有的日期：<br/>
 * <code>TimeInterval.newMonthInterval(new Date(), -1).{@link #getOccurDays()}</code><br/>
 * <b>用例</b>，下周最后一天：<br/>
 * <code>TimeInterval.newWeekInterval(new Date(), 1).{@link #getTo()}</code><br/>
 * 
 * @author ZhangXiaoye
 *
 */
public class TimeInterval {
	
	/**
	 * @param baseDate 基准时间
	 * @param daysOffsetFromBase 负数为基准向前多少天，正数为基准向后多少天，0为基准
	 * @return 表示1天的Interval，起点是当天00:00:00.000，终点是当天23:59:59.999
	 */
	public static TimeInterval newDayInterval(Date baseDate, int daysOffsetFromBase){
		Calendar cal = Calendar.getInstance();
		cal.setTime(baseDate);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		cal.add(Calendar.DATE, daysOffsetFromBase);
		Date from = cal.getTime();
		cal.set(Calendar.HOUR_OF_DAY, 23);
		cal.set(Calendar.MINUTE, 59);
		cal.set(Calendar.SECOND, 59);
		cal.set(Calendar.MILLISECOND, 999);
		return new TimeInterval(from, cal.getTime());
	}
	
	/**
	 * @param baseDate 基准时间
	 * @param daysOffsetFromBase 负数为基准向前多少周，正数为基准向后多少周，0为基准所在周
	 * @param isMondayFirstDayOfWeek 一周的第一天是周一还是周日（true表示周一）
	 * @return 表示1周的Interval，起点是周一00:00:00.000，终点是周日23:59:59.999
	 */
	public static TimeInterval newWeekInterval(Date baseDate, int weeksOffsetFromBase, boolean isMondayFirstDayOfWeek){
		Calendar cal = Calendar.getInstance();
		cal.setTime(baseDate);
		cal.set(Calendar.DAY_OF_WEEK, isMondayFirstDayOfWeek? Calendar.MONDAY: Calendar.SUNDAY);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		cal.add(Calendar.WEEK_OF_YEAR, weeksOffsetFromBase);
		Date from = cal.getTime();
		cal.add(Calendar.DATE, 6);
		cal.set(Calendar.HOUR_OF_DAY, 23);
		cal.set(Calendar.MINUTE, 59);
		cal.set(Calendar.SECOND, 59);
		cal.set(Calendar.MILLISECOND, 999);
		return new TimeInterval(from, cal.getTime());
	}
	
	/**
	 * @param baseDate 基准时间
	 * @param monthsOffsetFromBase 负数为基准向前多少月，正数为基准向后多少月，0为基准所在月
	 * @return 表示1月的Interval，起点是1号00:00:00.000，终点是月份最后一天的23:59:59.999
	 */
	public static TimeInterval newMonthInterval(Date baseDate, int monthsOffsetFromBase){
		Calendar cal = Calendar.getInstance();
		cal.setTime(baseDate);
		cal.set(Calendar.DAY_OF_MONTH, 1);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		cal.add(Calendar.MONTH, monthsOffsetFromBase);
		Date from = cal.getTime();
		cal.add(Calendar.MONTH, 1);
		cal.add(Calendar.MILLISECOND, -1);
		return new TimeInterval(from, cal.getTime());
	}
	
	/**
	 * @param baseDate 基准时间
	 * @param monthsOffsetFromBase 负数为基准向前多少年，正数为基准向后多少年，0为基准所在年
	 * @return 表示1年的Interval，起点是1月1日00:00:00.000，终点是12月31日23:59:59.999
	 */
	public static TimeInterval newYearInterval(Date baseDate, int yearsOffsetFromNow){
		Calendar cal = Calendar.getInstance();
		cal.setTime(baseDate);
		cal.set(Calendar.DAY_OF_YEAR, 1);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		cal.add(Calendar.YEAR, yearsOffsetFromNow);
		Date from = cal.getTime();
		cal.add(Calendar.YEAR, 1);
		cal.add(Calendar.MILLISECOND, -1);
		return new TimeInterval(from, cal.getTime());
	}
	
	private Date from;
	
	private Date to;
	
	/**
	 * 以当前时间初始化一个间隔为0的实例
	 */
	public TimeInterval(){
		this(new Date());
	}
	
	/**
	 * 以给定时间初始化一个间隔为0的实例
	 * @param from 给定时间
	 */
	public TimeInterval(Date from){
		this(from, from);
	}
	
	/**
	 * 以给定的开始时间和结束时间初始化一个实例
	 * 
	 * @param from 开始时间
	 * @param to 结束时间
	 */
	public TimeInterval(Date from, Date to){
		if(from == null){
			throw new NullPointerException("param:from can NOT be null.");
		}
		if(to == null){
			throw new NullPointerException("param:to can NOT be null.");
		}
		this.from = from;
		this.to = to;
	}
	
	/**
	 * 设置开始时间
	 * @param from 开始时间
	 * @return 支持链式调用
	 */
	public TimeInterval setFrom(Date from){
		if(from == null){
			throw new NullPointerException("param:from can NOT be null.");
		}
		this.from = from;
		return this;
	}
	
	/**
	 * @return 开始时间
	 */
	public Date getFrom() {
		return from;
	}
	
	/**
	 * 设置结束时间
	 * @param to 结束时间
	 * @return 支持链式调用
	 */
	public TimeInterval setTo(Date to){
		if(to == null){
			throw new NullPointerException("param:to can NOT be null.");
		}
		this.to = to;
		return this;
	}

	/**
	 * @return 结束时间
	 */
	public Date getTo() {
		return to;
	}

	/**
	 * 移动结束时间
	 * @param v 年数，可正可负
	 * @return 支持链式调用
	 */
	public TimeInterval addYears(int v){
		Calendar cal = Calendar.getInstance();
		cal.setTime(to);
		cal.add(Calendar.YEAR, v);
		to = cal.getTime();
		return this;
	}
	
	/**
	 * 移动结束时间
	 * @param v 月数，可正可负
	 * @return 支持链式调用
	 */
	public TimeInterval addMonths(int v){
		Calendar cal = Calendar.getInstance();
		cal.setTime(to);
		cal.add(Calendar.MONTH, v);
		to = cal.getTime();
		return this;
	}
	
	/**
	 * 移动结束时间
	 * @param v 周数，可正可负
	 * @return 支持链式调用
	 */
	public TimeInterval addWeeks(int v){
		Calendar cal = Calendar.getInstance();
		cal.setTime(to);
		cal.add(Calendar.WEEK_OF_YEAR, v);
		to = cal.getTime();
		return this;
	}
	
	/**
	 * 移动结束时间
	 * @param v  天数，可正可负
	 * @return 支持链式调用
	 */
	public TimeInterval addDays(int v){
		Calendar cal = Calendar.getInstance();
		cal.setTime(to);
		cal.add(Calendar.DATE, v);
		to = cal.getTime();
		return this;
	}
	
	/**
	 * 移动结束时间
	 * @param v  小时数，可正可负
	 * @return 支持链式调用
	 */
	public TimeInterval addHours(int v){
		to = new Date(to.getTime() + v * 3600l * 1000);
		return this;
	}
	
	/**
	 * 移动结束时间
	 * @param v  分钟，可正可负
	 * @return 支持链式调用
	 */
	public TimeInterval addMinutes(int v){
		to = new Date(to.getTime() + v * 60l * 1000);
		return this;
	}
	
	/**
	 * 移动结束时间
	 * @param v  秒数，可正可负
	 * @return 支持链式调用
	 */
	public TimeInterval addSeconds(int v){
		to = new Date(to.getTime() + v * 1000l);
		return this;
	}
	
	/** 同一年返回0，2016年12月31日到2017年1月1日返回1 */
	public int getYearDifferences(){
		Calendar cal = Calendar.getInstance();
		cal.setTime(from);
		int fYear = cal.get(Calendar.YEAR);
		cal.setTime(to);
		cal.add(Calendar.MILLISECOND, 1);
		int tYear = cal.get(Calendar.YEAR);
		return tYear - fYear;
	}
	
	/** 同一个月返回0，3月31到4月1日返回1 */
	public int getMonthDifferences(){
		Calendar cal = Calendar.getInstance();
		cal.setTime(from);
		int fYear = cal.get(Calendar.YEAR);
		int fMonth = cal.get(Calendar.MONTH);
		cal.setTime(to);
		cal.add(Calendar.MILLISECOND, 1);
		int tYear = cal.get(Calendar.YEAR);
		int tMonth = cal.get(Calendar.MONTH);
		return (tYear * 12 + tMonth) - (fYear * 12 + fMonth);
	}
	
	/** 同一周返回0，周末到下周第一天返回1 */
	public int getWeekDifferences(boolean isMondayFirstDayOfWeek){
		Calendar cal = Calendar.getInstance();
		cal.setTime(from);
		if(isMondayFirstDayOfWeek){
			cal.add(Calendar.DATE, -1);
		}
		cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
		long fT = cal.getTimeInMillis();
		cal.setTime(to);
		if(isMondayFirstDayOfWeek){
			cal.add(Calendar.DATE, -1);
		}
		cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
		long tT = cal.getTimeInMillis();
		return (int) ((tT - fT) / (7 * 24 * 3600l * 1000));
	}
	
	/** 不到1周（7天）返回0 */
	public int getWeeks(){
		return (int) ((to.getTime() - from.getTime() + 1) / (7 * 24 * 3600l * 1000));
	}
	
	/** 同一天返回0，23:59到第二天0:00返回1 */
	public int getDayDifferences(){
		long fT = clearTimePart(from).getTime();
		long tT = clearTimePart(to).getTime();
		return (int) ((tT - fT) / (24 * 3600l * 1000));
	}
	
	/** 不到1天（24小时）返回0 */
	public int getDays(){
		return (int) ((to.getTime() - from.getTime() + 1) / (24 * 3600l * 1000));
	}
	
	/** 同一小时返回0，18:59到19:01返回1 */
	public int getHourDifferences(){
		Calendar cal = Calendar.getInstance();
		cal.setTime(from);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		long f = cal.getTimeInMillis();
		cal.setTime(to);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		long t = cal.getTimeInMillis();
		return (int) ((t - f) / (3600l * 1000));
	}
	
	/** 不到1小时（60分钟）返回0 */
	public int getHours(){
		return (int) ((to.getTime() - from.getTime() + 1) / (3600 * 1000l));
	}
	
	/** 同一分钟返回0，01:01:59到01:02:00返回1 */
	public int getMinuteDifferences(){
		Calendar cal = Calendar.getInstance();
		cal.setTime(from);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		long f = cal.getTimeInMillis();
		cal.setTime(to);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		long t = cal.getTimeInMillis();
		return (int) ((t - f) / (1000));
	}
	
	/** 不到1分钟（60秒）返回0 */
	public int getMinutes(){
		return (int) ((to.getTime() - from.getTime() + 1) / (60 * 1000l));
	}
	
	/**
	 * @return 间隔中出现的所有日期，要求正向间隔，即{@link #getDayDifferences()} &gt;= 0
	 */
	public List<Date> getOccurDays(){
		long f = clearTimePart(from).getTime();
		long t = clearTimePart(to).getTime();
		List<Date> ret = new ArrayList<Date>();
		for(long i = f; i < t; i += 24 * 3600l * 1000){
			ret.add(new Date(i));
		}
		return ret;
	}
	
	/**
	 * @param format 格式
	 * @return 间隔中出现的所有日期，要求正向间隔，即{@link #getDayDifferences()} &gt;= 0
	 */
	public List<String> getOccurDays(DateFormat format){
		long f = clearTimePart(from).getTime();
		long t = clearTimePart(to).getTime();
		List<String> ret = new ArrayList<String>();
		for(long i = f; i <= t; i += 24 * 3600l * 1000){
			ret.add(format.format(new Date(i)));
		}
		return ret;
	}
	
	/**
	 * @param format 格式
	 * @return 间隔中出现的所有日期，要求正向间隔，即{@link #getDayDifferences()} &gt;= 0
	 */
	public List<String> getOccurDays(String format){
		if(Strings.isEmpty(format)){
			format = DateFormat.DEFAULT.getFormat();
		}
		long f = clearTimePart(from).getTime();
		long t = clearTimePart(to).getTime();
		List<String> ret = new ArrayList<String>();
		SimpleDateFormat formatter = new SimpleDateFormat(format);
		for(long i = f; i <= t; i += 24 * 3600l * 1000){
			ret.add(formatter.format(new Date(i)));
		}
		return ret;
	}
	
	/**
	 * @return 间隔中出现的所有小时，要求正向间隔，即{@link #getHourDifferences()} &gt;= 0
	 */
	public List<Date> getOccurHours(){
		long f = clearCalendarFields(from, Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND).getTime();
		long t = clearCalendarFields(to, Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND).getTime();
		List<Date> ret = new ArrayList<Date>();
		for(long i = f; i <= t; i += 3600l * 1000){
			ret.add(new Date(i));
		}
		return ret;
	}
	
	/**
	 * @param format 格式
	 * @return 间隔中出现的所有小时，要求正向间隔，即{@link #getHourDifferences()} &gt;= 0
	 */
	public List<String> getOccurHours(DateFormat format){
		long f = clearCalendarFields(from, Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND).getTime();
		long t = clearCalendarFields(to, Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND).getTime();
		List<String> ret = new ArrayList<String>();
		for(long i = f; i <= t; i += 3600l * 1000){
			ret.add(format.format(new Date(i)));
		}
		return ret;
	}
	
	/**
	 * @param format 格式
	 * @return 间隔中出现的所有小时，要求正向间隔，即{@link #getHourDifferences()} &gt;= 0
	 */
	public List<String> getOccurHours(String format){
		if(Strings.isEmpty(format)){
			format = DateFormat.DEFAULT.getFormat();
		}
		long f = clearCalendarFields(from, Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND).getTime();
		long t = clearCalendarFields(to, Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND).getTime();
		List<String> ret = new ArrayList<String>();
		SimpleDateFormat formatter = new SimpleDateFormat(format);
		for(long i = f; i <= t; i += 3600l * 1000){
			ret.add(formatter.format(new Date(i)));
		}
		return ret;
	}
	
	/**
	 * @return 间隔值的绝对值，以{@link Date}格式表示。可以格式化成字符串。
	 */
	public Date getAbsInterval(){
		Date from = this.to.compareTo(this.from) < 0? this.to: this.from;
		Date to = this.to.compareTo(this.from) < 0? this.from: this.to;
		return new Date(to.getTime() - from.getTime());
	}
	
	/**
	 * @param date 日期时间
	 * @param calendarFields 指定要清除的字段，如{@link Calendar#HOUR_OF_DAY}
	 * @return 清除指定部分的日期时间
	 */
	public static Date clearCalendarFields(Date date, int... calendarFields){
		if(calendarFields.length <= 0){
			return date;
		}
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		for(int calField: calendarFields){
			if(calField == Calendar.AM_PM || calField == Calendar.HOUR || calField == Calendar.HOUR_OF_DAY){
				cal.set(Calendar.HOUR_OF_DAY, 0);
			}else if(calField == Calendar.DAY_OF_MONTH){
				cal.set(Calendar.DAY_OF_MONTH, 1);
			}else if(calField == Calendar.DAY_OF_YEAR){
				cal.set(Calendar.MONDAY, 0);
				cal.set(Calendar.DAY_OF_MONTH, 1);
			}else{
				cal.clear(calField);
			}
		}
		return cal.getTime();
	}
	
	/**
	 * @param date 日期时间
	 * @return 只保留日期部分，时间清零
	 */
	public static Date clearTimePart(Date date){
		return clearCalendarFields(date, Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND);
	}
	
}
